package com.sishu.njrtsdms.utils;

import cn.hutool.core.util.StrUtil;
import com.sishu.njrtsdms.constant.Constants;
import com.sishu.njrtsdms.constant.user.UserConstants;
import com.sishu.njrtsdms.domain.core.LoginUser;
import org.hibernate.service.spi.ServiceException;
import org.jooq.types.ULong;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.Optional;

/**
 * 安全服务工具类
 *
 * @author njrts-dms
 */
public class SecurityUtils {
    /**
     * 用户ID
     **/
    public static ULong getUserId() {
        try {
            return getLoginUserOptional()
                    .map(it -> ULong.valueOf(it.getUserId()))
                    .orElse(ULong.valueOf(Constants.SYSTEM_LOGIN_USER_INSTANCE.getUserId()));
        } catch (Exception e) {
            throw new ServiceException("获取用户ID异常");
        }
    }

    /**
     * 获取部门ID
     **/
    public static ULong getCollegeId() {
        try {
            return getLoginUserOptional()
                    .orElseThrow()
                    .getUser()
                    .college()
                    .collegeId();

        } catch (Exception e) {
            throw new ServiceException("获取学院ID异常");
        }
    }

    /**
     * 获取用户账户
     **/
    public static String getUsername() {
        try {
            return getLoginUserOptional()
                    .map(LoginUser::getUsername)
                    .orElse(null);
        } catch (Exception e) {
            throw new ServiceException("获取用户账户异常");
        }
    }

    /**
     * 获取用户
     **/
    public static Optional<LoginUser> getLoginUserOptional() {
        Optional<LoginUser> loginUserOpt;
        try {
            if (getAuthentication() == null || isAnonymousUser())
                loginUserOpt = Optional.of(Constants.SYSTEM_LOGIN_USER_INSTANCE);
            else
                loginUserOpt = Optional.of((LoginUser) getAuthentication().getPrincipal());
            return loginUserOpt;
        } catch (Exception e) {
            throw new ServiceException("获取用户信息异常");
        }

    }

    /**
     * 获取Authentication
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    /**
     * 判断密码是否相同
     *
     * @param rawPassword     真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword,
                                          String encodedPassword) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }


    /**
     * 是否是匿名用户
     */
    public static boolean isAnonymousUser() {
        if (getAuthentication() == null) return false;
        return StrUtil.equals(UserConstants.ANONYMOUS_USER_FLAG,
                getAuthentication().getPrincipal().toString());
    }


    /**
     * 加密用户默认密码,新用户不指定密码时默认密码为1
     */
    private static String encryptPasswordForDefaultPassword() {
        return encryptPassword(UserConstants.USER_DEFAULT_PASSWORD);
    }


    /**
     * 生成存储入数据库中的加密后的密码
     * <p>
     * 当传入的密码不为空时,直接生成加密密码返回。
     * 反之判断是否是新用户,如果是新用户则生成默认的加密密码,不是新用户则返回null,防止修改
     *
     * @param rawPassword  传入的明文密码
     * @param isForNewUser 是否是为了插入新用户
     * @return 加密后的密码
     */
    public static String generateDBStorePassword(String rawPassword, boolean isForNewUser) {
        if (StrUtil.isNotBlank(rawPassword))
            return encryptPassword(rawPassword);
        if (isForNewUser)
            return encryptPasswordForDefaultPassword();
        else
            return null;
    }


    /**
     * 是否为管理员
     *
     * @param userId 用户ID
     * @return 结果
     */
    public static boolean isAdmin(Long userId) {
        // todo 需要的话实现此方法
        // ...
        return true;
    }
}
